--- /dev/null
+/*
+ * Copyright © 2019 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtksignallistitemfactory.h"
+
+#include "gtkintl.h"
+#include "gtklistitemfactoryprivate.h"
+#include "gtklistitem.h"
+
+/**
+ * SECTION:gtksignallistitemfactory
+ * @title: GtkSignalListItemFactory
+ * @short_description: A listitem factory providing signals
+ * @see_also: #GtkListItemFactory
+ *
+ * #GtkSignalListItemFactory is a #GtkListItemFactory that provides signals
+ * that user code can connect to to manage listitems.
+ * Signals are emitted for every listitem in the same order:
+ *
+ * 1. #GtkSignalListItemFactory::setup is emitted to set up permanent things
+ * on the listitem. This usually means constructing the widgets used in the
+ * row and adding them to the listitem.
+ *
+ * 2. #GtkSignalListItemFactory::bind is emitted to bind the item passed via
+ * #GtkListItem:item to the widgets that have been created in step 1 or to
+ * add item-specific widgets. Signals are connected to listen to changes -
+ * both to changes in the item to update the widgets or to changes in the
+ * widgets to update the item. After this signal has been called, the
+ * listitem may be shown in a list widget.
+ *
+ * 3. #GtkSignalListItemFactory::unbind is emitted to undo everything done
+ * in step 2. Usually this means disconnecting signal handlers. Once this
+ * signal has been called, the listitem will no longer be used in a list
+ * widget.
+ *
+ * 4. #GtkSignalListItemFactory::bind and #GtkSignalListItemFactory::unbind
+ * may be emitted multiple times again to bind the listitem for use with
+ * new items. By reusing listitems, potentially costly setup can be
+ * avoided. However, it means code needs to make sure to properly clean
+ * up the listitem in step 3 so that no information from the previous
+ * use leaks into the next use.
+ *
+ * 5. #GtkSignalListItemFactory::teardown is emitted to allow undoing the
+ * effects of #GtkSignalListItemFactory::setup. After this signal was emitted
+ * on a listitem, the listitem will be destroyed and not be used again.
+ *
+ * Note that during the signal emissions, changing properties on the
+ * #GtkListItems passed will not trigger notify signals as the listitem's
+ * notifications are frozen. See g_object_freeze_notify() for details.
+ *
+ * For tracking changes in other properties in the #GtkListItem, the
+ * #GtkListItem::notify signal is recommended. The signal can be connected
+ * in the #GtkSignalListItemFactory::setup signal and removed again during
+ * #GtkSignalListItemFactory::teardown.
+ */
+
+struct _GtkSignalListItemFactory
+{
+ GtkListItemFactory parent_instance;
+};
+
+struct _GtkSignalListItemFactoryClass
+{
+ GtkListItemFactoryClass parent_class;
+
+ void (* setup) (GtkListItemFactory *self,
+ GtkListItem *list_item);
+ void (* teardown) (GtkListItemFactory *self,
+ GtkListItem *list_item);
+
+ void (* bind) (GtkListItemFactory *self,
+ GtkListItem *list_item,
+ guint position,
+ gpointer item,
+ gboolean selected);
+ void (* unbind) (GtkListItemFactory *self,
+ GtkListItem *list_item);
+};
+
+enum {
+ SETUP,
+ BIND,
+ UNBIND,
+ TEARDOWN,
+
+ LAST_SIGNAL
+};
+
+G_DEFINE_TYPE (GtkSignalListItemFactory, gtk_signal_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+gtk_signal_list_item_factory_setup (GtkListItemFactory *factory,
+ GtkListItem *list_item)
+{
+ GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->setup (factory, list_item);
+
+ g_signal_emit (factory, signals[SETUP], 0, list_item);
+}
+
+static void
+gtk_signal_list_item_factory_bind (GtkListItemFactory *factory,
+ GtkListItem *list_item,
+ guint position,
+ gpointer item,
+ gboolean selected)
+{
+ GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->bind (factory, list_item, position, item, selected);
+
+ g_signal_emit (factory, signals[BIND], 0, list_item);
+}
+
+static void
+gtk_signal_list_item_factory_rebind (GtkListItemFactory *factory,
+ GtkListItem *list_item,
+ guint position,
+ gpointer item,
+ gboolean selected)
+{
+ g_signal_emit (factory, signals[UNBIND], 0, list_item);
+
+ GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->bind (factory, list_item, position, item, selected);
+
+ g_signal_emit (factory, signals[BIND], 0, list_item);
+}
+
+static void
+gtk_signal_list_item_factory_unbind (GtkListItemFactory *factory,
+ GtkListItem *list_item)
+{
+ g_signal_emit (factory, signals[UNBIND], 0, list_item);
+
+ GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->unbind (factory, list_item);
+}
+
+static void
+gtk_signal_list_item_factory_teardown (GtkListItemFactory *factory,
+ GtkListItem *list_item)
+{
+ g_signal_emit (factory, signals[TEARDOWN], 0, list_item);
+
+ GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, list_item);
+}
+
+static void
+gtk_signal_list_item_factory_class_init (GtkSignalListItemFactoryClass *klass)
+{
+ GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
+
+ factory_class->setup = gtk_signal_list_item_factory_setup;
+ factory_class->bind = gtk_signal_list_item_factory_bind;
+ factory_class->rebind = gtk_signal_list_item_factory_rebind;
+ factory_class->unbind = gtk_signal_list_item_factory_unbind;
+ factory_class->teardown = gtk_signal_list_item_factory_teardown;
+
+ /**
+ * GtkSignalListItemFactory::setup:
+ * @self: The #GtkSignalListItemFactory
+ * @listitem: The #GtkListItem to set up
+ *
+ * The ::setup signal is emitted when a new listitem has been created and
+ * needs to be setup for use. It is the first signal emitted for every listitem.
+ *
+ * The GtkSignalListItemFactory::teardown signal is the opposite of this signal
+ * and can be used to undo everything done in this signal.
+ */
+ signals[SETUP] =
+ g_signal_new (I_("setup"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, setup),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_LIST_ITEM);
+ g_signal_set_va_marshaller (signals[SETUP],
+ G_TYPE_FROM_CLASS (klass),
+ g_cclosure_marshal_VOID__OBJECTv);
+
+ /**
+ * GtkSignalListItemFactory::bind:
+ * @self: The #GtkSignalListItemFactory
+ * @listitem: The #GtkListItem to bind
+ *
+ * The ::bind signal is emitted when a new GtkListItem:item has been set
+ * on the @listitem and should be bound for use.
+ *
+ * After this signal was emitted, the listitem might be shown in a #GtkListView
+ * or other list widget.
+ *
+ * The GtkSignalListItemFactory::unbind signal is the opposite of this signal
+ * and can be used to undo everything done in this signal.
+ */
+ signals[BIND] =
+ g_signal_new (I_("bind"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, bind),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_LIST_ITEM);
+ g_signal_set_va_marshaller (signals[BIND],
+ G_TYPE_FROM_CLASS (klass),
+ g_cclosure_marshal_VOID__OBJECTv);
+
+ /**
+ * GtkSignalListItemFactory::unbind:
+ * @self: The #GtkSignalListItemFactory
+ * @listitem: The #GtkListItem to unbind
+ *
+ * The ::unbind signal is emitted when a listitem has been removed from use
+ * in a list widget and its new GtkListItem:item is about to be unset.
+ *
+ * This signal is the opposite of the GtkSignalListItemFactory::bind signal
+ * and should be used to undo everything done in that signal.
+ */
+ signals[UNBIND] =
+ g_signal_new (I_("unbind"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, unbind),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_LIST_ITEM);
+ g_signal_set_va_marshaller (signals[UNBIND],
+ G_TYPE_FROM_CLASS (klass),
+ g_cclosure_marshal_VOID__OBJECTv);
+
+ /**
+ * GtkSignalListItemFactory::teardown:
+ * @self: The #GtkSignalListItemFactory
+ * @listitem: The #GtkListItem to teardown
+ *
+ * The ::teardown signal is emitted when a listitem is about to be destroyed.
+ * It is the last signal ever emitted for this @listitem.
+ *
+ * This signal is the opposite of the GtkSignalListItemFactory::setup signal
+ * and should be used to undo everything done in that signal.
+ */
+ signals[TEARDOWN] =
+ g_signal_new (I_("teardown"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, teardown),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_LIST_ITEM);
+ g_signal_set_va_marshaller (signals[TEARDOWN],
+ G_TYPE_FROM_CLASS (klass),
+ g_cclosure_marshal_VOID__OBJECTv);
+}
+
+static void
+gtk_signal_list_item_factory_init (GtkSignalListItemFactory *self)
+{
+}
+
+/**
+ * gtk_signal_list_item_factory_new:
+ *
+ * Creates a new #GtkSignalListItemFactory. You need to connect signal
+ * handlers before you use it.
+ *
+ * Returns: a new #GtkSignalListItemFactory
+ **/
+GtkListItemFactory *
+gtk_signal_list_item_factory_new (void)
+{
+ return g_object_new (GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY, NULL);
+}
+